﻿using UnityEngine;
using System.Collections.Generic;
using CinemaDirector;
using TTGame;
/*
 * SkillTL
 * 技能类
 *
 * 收集记录技能信息、技能的Cutscene对象。
 * 对外提供技能的所有接口，如信息读取。
 *
 *
 * 完整的技能对象，包含技能脚本（Skill）、动画和触发器（Trigger）。
 * Trigger触发时，通过获取技能信息，生成子弹或者碰撞框。
 * Trigger产生的碰撞框发生碰撞时，将碰撞对象交由Skill进行伤害处理。
 * Skill的伤害处理，是为了防止重复伤害对象。
 *
 *
 *
 *
 * 依赖：
 * SkillData, Puppet
 *
 * ！不写重复代码！
 * @author TTun
 * @date 7/17/2017 11:25:28 AM
 */
namespace TTGameEngine {
public enum SkillCheckResult {
    SkillRet_OK,                // 表示技能正在顺利执行。可以向下继续执行。
    SkillRet_CastFinish,        // 施法成功
    SkillRet_WaitSelectTarget,  // 等待选择目标阶段
    SkillRet_SkillCasting,     // 等待释放阶段
    SkillRet_RunningWell,     // 以上是正常运行阶段
    SkillRet_INTR_Fail_Releasing,    // 玩家正在施法中，且之前的技能无法打断
    SkillRet_Fail_NoSkill,      // 装载技能失败
    SkillRet_Fail_Busying,      // 当前状态无法施法
    SkillRet_Fail_RangeError,   // 施法范围不对
    SkillRet_Fail_TargetError,  // 没有目标或者目标不对。
    SkillRet_Fail_Cooldown,     // 技能冷却中
    SkillRet_Fail_NeedReload,   // 需要装载子弹
    SkillRet_Fail_TimeFreezing, // 人物暂停状态不能施法
    SkillRet_Fail_Unkown,       // 未知原因失败
    SkillRet_Fail_ObjNotActived,       // 对象未激活
    SkillRet_Fail_AngerInsufficient, //怒气不足
    SkillRet_Fail_CastCountZero,     // 技能的释放次数不够
    /// <summary>
    /// 错误：技能脚本正在播放。技能释放前，尝试中断过当前技能，不应该出现这种情况。
    /// </summary>
    SkillRet_Exception_NullSkill,         // skill为空，具体不知道为什么
    SkillRet_Exception_SkillPlaying,     //技能脚本内部状态是运行中，导致无法运行新技能
    SkillRet_Exception_SkillStateError,  //技能脚本内部状态错误
}

public enum CheckTargetStrategy {
    AlwaysCheck,
    CheckOnceAtFirst,
    LazyCheck,  //需要的时候进行第一次目标获取，之后不再获取。
}


public delegate void OnSkillCastBegin(SkillTL skillTL);
public delegate void OnSkillFinished(SkillTL skillTL);

public class SkillTL : MonoBehaviour {
    #region 预设参数
    [SerializeField]
    int m_skillID;
    [SerializeField]
    Cutscene m_cutSc;
    [SerializeField]
    public SkillData m_skillData;
    [SerializeField]
    CheckTargetStrategy m_checkTargetStrategy = CheckTargetStrategy.LazyCheck;
    #endregion

    [SerializeField]
    public Puppet m_caster;
    Puppet debug_caster;
    //剩余可释放次数
    public int lastCastCount = int.MaxValue;
    [ReadOnly]
    public SkillSelector m_selector = new SkillSelector();

    float t_curCDTime = 0; //CD倒计时
    bool m_noMoreCheck = false;


    List<Puppet> targetObjList = new List<Puppet>();

    [SerializeField, ReadOnly]
    int m_curBulletCount = 0;
    uint m_castID = 0;

    public static CountID_UInt SkillGlobalID = new CountID_UInt();
    SkillHandler m_skillLogicHandler = null;

    OnSkillCastBegin cb_skillCastBegin;
    OnSkillFinished cb_skillFinished;

    public SkillHandler SkillLogicHandler {
        get {
            checkSkillHandler();
            return m_skillLogicHandler;
        }
    }

    public int BulletCount {
        set {
            m_curBulletCount = value;
            if(m_curBulletCount <= 0) {
                m_curBulletCount = 0;
                SkillLog("技能子弹用完了");
                BreakLoop(false);
            }
        }
        get { return m_curBulletCount; }
    }

    public int SkillID {
        get { return m_skillID; }
    }
    public uint SkillCastID {
        get { return m_castID; }
    }
    public SkillType SkillType {
        get { return m_skillData.m_skillType; }
    }

    void Awake() {
        if(m_cutSc == null) m_cutSc = GetComponentInChildren<Cutscene>();
        if(m_skillData == null) m_skillData = GetComponent<SkillData>();

        m_cutSc.CutsceneFinished += OnCutsceneFinished;

        SkillSelectorType selType = m_skillData.m_selectorType;
        switch(selType) {
            case SkillSelectorType.Sector:// 扇型
                m_selector.InitSelector_Sector(selType, m_skillData.m_selectorRadius, m_skillData.m_angle);
                break;
            case SkillSelectorType.Direct:// 方向型
                m_selector.InitSelector_Direct(selType, m_skillData.m_selectorRadius, m_skillData.m_width);
                break;
            case SkillSelectorType.Area:// 范围型
                m_selector.InitSelector_Area(selType, m_skillData.m_selectorRadius, m_skillData.m_hitRadius);
                break;
            case SkillSelectorType.Lock:// 选择型
                m_selector.InitSelector_Lock(selType, m_skillData.m_selectorRadius);
                break;
            case SkillSelectorType.SelfCenter:// 自身范围
                m_selector.InitSelector_SelfCenter(selType, m_skillData.m_selectorRadius);
                break;
        }

        m_curBulletCount = m_skillData.MaxBulletCapacity;
    }

    public void InitWithServerData() {
//         var skillSData = BattleServerData.Share().GetSkillSData(m_skillID);
//         if(skillSData != null) {
//             m_skillData.skillName = skillSData.name;
//             m_skillData.skillClass = skillSData.skillClass;
//             m_skillData.level = skillSData.lv;
//             m_skillData.m_CDTime = skillSData.cd;
//             m_skillData.angerConsume = skillSData.consume;
//             m_skillData.angerAdd = skillSData.anger;
//             m_skillData.m_hurtType = skillSData.hurt_type;
//             m_skillData.maxHitTarget = skillSData.max;
//             m_skillData.dmgRatio = skillSData.ratio;
//             m_skillData.dmgAdd = skillSData.add;
//             m_skillData.crit = skillSData.crit;
//             m_skillData.buffProbs = skillSData.buffProbs;
//             m_skillData.skillDmgType = skillSData.skillDmgType;
//
//             if(m_skillData.maxHitTarget == 0) {
//                 m_skillData.maxHitTarget = 1;
//             }
//         }
    }

    // Update is called once per frame
    void Update() {
        float dt = Time.deltaTime;
        if(t_curCDTime > 0) t_curCDTime -= dt;
    }

    //用于log输出
    public void Debug_SetCaster(Puppet caster) {
        debug_caster = caster;
    }

    public void RegistCB_SkillCastBegin(OnSkillCastBegin cb) {
        cb_skillCastBegin -= cb;
        cb_skillCastBegin += cb;
    }

    public void RegistCB_SkillFinished(OnSkillFinished cb) {
        cb_skillFinished -= cb;
        cb_skillFinished += cb;
    }

    bool checkSkillHandler() {
        if(m_skillLogicHandler == null) {
            SkillLogError("m_skillLogicHandler is null.");
            return false;
        }
        return true;
    }

    protected void Reset() {
        SkillLog("skill reset");
        if(m_skillData.IsNeedReloadLikeAGun == false) {
            m_curBulletCount = m_skillData.MaxBulletCapacity;
        }

        m_noMoreCheck = false;
        if(SkillPlaying == true) {
            SkillLogError("技能正在运行中，就被reset了！");
        }
        m_skillLogicHandler = null;
        SkillLog("m_skillLogicHandler = null");
        targetObjList.Clear();
        //m_caster = null;
        //debug_caster = null;
    }

    public void ResetCastID() {
        m_castID = SkillGlobalID.GetGID();
    }

    bool SkillPlaying = false;
    bool Check_SkillBegining = false;
    public SkillCheckResult CastSkill(Puppet caster) {
        SkillPlaying = false;
        SkillCheckResult ret = SkillCheckResult.SkillRet_OK;

        if(lastCastCount <= 0) {
            ret = SkillCheckResult.SkillRet_Fail_CastCountZero;
            return ret;
        }
        if(m_cutSc.State != Cutscene.CutsceneState.Playing) {
            //脚本的time有时候没有清空，需要调用stop。不然Play以后会立刻结束。
            m_cutSc.Stop();
            Reset();
            if(m_checkTargetStrategy == CheckTargetStrategy.AlwaysCheck
                    || m_checkTargetStrategy == CheckTargetStrategy.CheckOnceAtFirst) {
                CheckTargets();
            }
            //m_caster = caster;
            m_cutSc.SetActorGameObject("Actor Track Group", "", m_caster.gameObject);
            SkillLog("before Cast Play . CutSc.State:[{0}] time:[{1}] nextRunTime:[{2}] durTime:[{3}]",
                     m_cutSc.State.ToString(),
                     m_cutSc.RunningTime,
                     m_cutSc.nextRunningTime,
                     m_cutSc.Duration
                    );

            m_skillLogicHandler = new SkillHandler(m_castID, m_skillData);
            SkillLog("m_skillLogicHandler New");
            applyServerBuff(caster);
            SkillPlaying = true;
            Check_SkillBegining = true;
            m_cutSc.Play();
            Check_SkillBegining = false;
            SkillLog("Cast Play. CutSc.State:[{0}] time:[{1}]]", m_cutSc.State.ToString(), m_cutSc.RunningTime);
            if(m_cutSc.State != Cutscene.CutsceneState.Playing) {
                SkillLogError("Cast Play Error");
                ret = SkillCheckResult.SkillRet_Exception_SkillStateError;
            } else {
                lastCastCount--;
            }
        } else {
            SkillLog("Cast Fail. skill is playing");
            ret = SkillCheckResult.SkillRet_Exception_SkillPlaying;
        }
        return ret;
    }

    void applyServerBuff(Puppet caster) {
        if(m_skillData.buffProbs != null && m_skillData.buffProbs.Count > 0) {
            for(var ite = m_skillData.buffProbs.GetEnumerator(); ite.MoveNext();) {
                int buffID = ite.Current.Key;
                BuffTrigger.TriggerBuff(caster, this, buffID);
            }
        }
    }

    /// <summary>
    ///
    /// </summary>
    /// <returns>是否可以被打断</returns>
    public bool TryInterrupt() {
        SkillLog("打断技能");
        StopSkill();
        return true;
    }

    public void StopSkill() {
        SkillLog("调用停止技能脚本");
        if(Check_SkillBegining) {
            SkillLogError("不要在技能脚本开始时，调用停止技能。会导致【技能开始事件】无法触发。技能开始事件中目前包含怒气扣除逻辑。");
        }
        Check_SkillBegining = false;
        m_cutSc.Stop();
        if(m_cutSc.State == Cutscene.CutsceneState.Playing) {
            SkillLogError("Skill脚本停止，但是状态仍然是Playing");
        }
        OnSkillStop();
    }

    void OnSkillStop() {
        if(SkillPlaying == false) {
            return;
        }

        SkillPlaying = false;
        BreakLoop(true);
        Reset();
        //技能结束后，设置CD;
        t_curCDTime = m_skillData.m_CDTime;
        if(m_caster != null) m_caster.SetOnGuardState();
    }

    public void BreakLoop(bool skipCurAnim) {
        SkillLog("Skill BreakLoop");
        if(m_skillLoopAnimEvent != null) {
            m_skillLoopAnimEvent.BreakLoop(skipCurAnim);
            m_skillLoopAnimEvent = null;
        }
    }

    /// <summary>
    /// 在技能播放前，为了初始化技能状态，会调用cutscene.Stop()导致触发回调.
    /// 这里使用SkillPlaying标志，保证只有技能结束情况才会触发结束回调。
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public void OnCutsceneFinished(object sender, CutsceneEventArgs e) {
        SkillLog("技能脚本结束");
        OnSkillStop();
    }

    public bool IsSkillCutSceneEnd() {
        if(m_cutSc.State == Cutscene.CutsceneState.Playing || m_cutSc.State == Cutscene.CutsceneState.Paused) {
            return false;
        }
        return true;
    }

    public bool IsStopped() {
        return !SkillPlaying;
    }

    public SkillSelectorType GetSelectorType() {
        return m_selector.SelectorType;
    }

    public float GetSkillCastRange() {
        return m_selector.SelectorRadius;
    }

    public void RegistCB_SkillHarmTarget(OnSkillHarmTarget cb) {
        checkSkillHandler();
        m_skillLogicHandler.RegistCB_SkillDetectTarget(cb);
    }

    public void UnRegistCB_SkillHarmTarget(OnSkillHarmTarget cb) {
        checkSkillHandler();
        m_skillLogicHandler.UnRegistCB_SkillDetectTarget(cb);
    }

    public void RegistBulletLogicHandler(MagicBall mb) {
        if(mb == null || checkSkillHandler() == false) {
            return;
        }
        m_skillLogicHandler.RegistBullet(mb);
    }

    /// <summary>
    /// 技能是否准备好。
    /// </summary>
    /// <returns></returns>
    public bool IsPrepared() {
        if(IsCooldownOK() == false || lastCastCount <= 0) {
            return false;
        }

        return true;
    }

    /// <summary>
    /// 检测是否可以施法
    /// </summary>
    /// <returns></returns>
    public SkillCheckResult CheckCast(Puppet caster) {
        //技能CD
        if(IsCooldownOK() == false) {
            return SkillCheckResult.SkillRet_Fail_Cooldown;
        }
        //技能需要装载子弹
        if(IsNeedReload()) {
            return SkillCheckResult.SkillRet_Fail_NeedReload;
        }
        //检查怒气
        if(caster.GetPropWithBuff(PuppetIntPropID.Prop_curAnger) < m_skillData.angerConsume) {
            return SkillCheckResult.SkillRet_Fail_AngerInsufficient;
        }
        return SkillCheckResult.SkillRet_OK;
    }

    public virtual void Revert() {
    }

    public bool IsCooldownOK() {
        return t_curCDTime <= 0;
    }

    public List<Puppet> GetTargetObjs() {
        if(m_noMoreCheck == false
                && (m_checkTargetStrategy == CheckTargetStrategy.AlwaysCheck
                    || m_checkTargetStrategy == CheckTargetStrategy.LazyCheck)) {
            CheckTargets();
            if(m_checkTargetStrategy == CheckTargetStrategy.LazyCheck) {
                m_noMoreCheck = true;
            }
        }
        return targetObjList;
    }

    public int GetHarmObjsCount() {
        if(false == checkSkillHandler()) {
            return 0;
        }
        return m_skillLogicHandler.GetHarmObjsCount();
    }


    /// <summary>
    /// 检测目标
    ///
    /// mark:add 在某一阶段之后，不再重新获取对象。使用已经锁定的那些对象。
    /// 如：
    /// 策略1、一直都是实时选择目标
    /// 策略2、技能触发以后选择一次目标，之后锁定这些目标，不再选择。
    /// 策略3、技能中分阶段，每阶段重新获取目标
    /// 策略4、添加重新获取目标触发器。手动控制重新获取目标的阶段。
    ///
    /// 目前是策略1。每次需要目标时重新获取，相当于实时获取目标了。
    /// </summary>
    /// <returns></returns>
    public List<Puppet> CheckTargets() {
        targetObjList.Clear();
        ObjectManager.Share().GetObjsWithSelectorResult(m_selector, m_caster.m_objType, targetObjList);
        return targetObjList;
    }

    public void Pause() {
        SkillLog("！暂停技能！");
        m_cutSc.Pause();
    }

    public void Resume() {
        SkillLog("！恢复技能！");
        m_cutSc.Play();
    }
    public void triggerSkillBeigin() {
        if(cb_skillCastBegin != null) cb_skillCastBegin(this);
    }

    public void triggerSkillFinished() {
        if(cb_skillFinished != null) cb_skillFinished(this);
    }

    SkillPlayAnimEvent m_skillLoopAnimEvent;

    public void RegistLoopAnimEvent(SkillPlayAnimEvent skillAnimEvent) {
        m_skillLoopAnimEvent = skillAnimEvent;
    }

    public bool IsLoopSkill() {
        if(m_skillLoopAnimEvent != null) {
            return m_skillLoopAnimEvent.isLoop;
        }
        return false;
    }

    void SkillLog(string msgformat, params object[] args) {

        if(SkillTimelineSystem.SkillSystemLogFlag == false) {
            return;
        }
        DebugUtil.LogFormat("[Skill]{0}\n{1}",
                            string.Format(msgformat, args),
                            DumpSkill());
    }

    void SkillLogError(string msgformat, params object[] args) {

        DebugUtil.LogErrorFormat("[Skill]{0}\n{1}",
                                 string.Format(msgformat, args),
                                 DumpSkill());
    }

    public string DumpSkill() {
        Puppet caster = m_caster;
        if(caster == null) {
            caster = debug_caster;
        }
        return string.Format("obj[{0}] skillID[{1}] CastID[{2}] time:[{3}/{4}]",
                             caster != null ? caster.GetGID() : "No Caster",
                             SkillID,
                             SkillCastID,
                             m_cutSc != null ? m_cutSc.RunningTime.ToString() : "0",
                             m_cutSc != null ? m_cutSc.Duration.ToString() : "0"
                            );
    }

    public bool IsNeedReload() {
        if(m_skillData.IsNeedReloadLikeAGun && BulletCount <= 0) {
            return true;
        }
        return false;
    }

    public void FillBullet(AmmoPouch ammo) {
        if(m_skillData.IsNeedReloadLikeAGun == false || ammo.IsInfinitiBullet) {
            m_curBulletCount = m_skillData.MaxBulletCapacity;
        } else {
            if(ammo != null) {
                int unfilledBullet = m_skillData.MaxBulletCapacity - m_curBulletCount;
                if(ammo.bulletCount < unfilledBullet) {
                    m_curBulletCount += ammo.bulletCount;
                    ammo.bulletCount = 0;
                } else {
                    m_curBulletCount = m_skillData.MaxBulletCapacity;
                    ammo.bulletCount -= unfilledBullet;
                }
            }
        }
    }

}
}